///*
// * Copyright The OpenTelemetry Authors
// * SPDX-License-Identifier: Apache-2.0
// */
//
//package com.xiashitech.agent.instrumentation.netty.netty_v4_1.inst;
//
//import com.xiashitech.agent.instrumentation.netty.netty_v4_1.client.HttpClientRequestTracingHandler;
//import com.xiashitech.agent.instrumentation.netty.netty_v4_1.client.HttpClientResponseTracingHandler;
//import com.xiashitech.agent.instrumentation.netty.netty_v4_1.server.HttpServerRequestTracingHandler;
//import com.xiashitech.agent.instrumentation.netty.netty_v4_1.server.HttpServerResponseTracingHandler;
//import com.xiashitech.agent.instrumentation.netty.netty_v4_common.client.NettySslInstrumentationHandler;
//import com.xiashitech.agent.instrumentation.netty.netty_v4_common.inst.AbstractNettyChannelPipelineInstrumentation;
//import io.netty.channel.ChannelHandler;
//import io.netty.channel.ChannelHandlerContext;
//import io.netty.channel.ChannelPipeline;
//import io.netty.handler.codec.http.*;
//import io.opentelemetry.javaagent.bootstrap.CallDepth;
//import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
//import io.opentelemetry.javaagent.instrumentation.netty.v4_1.client.HttpClientTracingHandler;
//import io.opentelemetry.javaagent.instrumentation.netty.v4_1.server.HttpServerTracingHandler;
//import io.opentelemetry.javaagent.shaded.instrumentation.api.util.VirtualField;
//import net.bytebuddy.asm.Advice;
//
//import static com.xiashitech.agent.instrumentation.netty.netty_v4_1.client.NettyClientSingletons.sslInstrumenter;
//import static net.bytebuddy.matcher.ElementMatchers.*;
//
//public class NettyChannelPipelineInstrumentation
//        extends AbstractNettyChannelPipelineInstrumentation {
//
//    @Override
//    public void transform(TypeTransformer transformer) {
//        super.transform(transformer);
//
//        transformer.applyAdviceToMethod(
//                isMethod()
//                        .and(nameStartsWith("add").or(named("replace")))
//                        .and(takesArgument(1, String.class))
//                        .and(takesArgument(2, named("io.netty.channel.ChannelHandler"))),
//                NettyChannelPipelineInstrumentation.class.getName() + "$ChannelPipelineAddAdvice");
//    }
//
//    /**
//     * When certain handlers are added to the pipeline, we want to add our corresponding tracing
//     * handlers. If those handlers are later removed, we also remove our handlers. Support for
//     * replacing handlers and removeFirst/removeLast is currently not implemented.
//     */
//    @SuppressWarnings("unused")
//    public static class ChannelPipelineAddAdvice {
//
//        @Advice.OnMethodEnter
//        public static void trackCallDepth(
//                @Advice.Argument(2) ChannelHandler handler,
//                @Advice.Local("otelCallDepth") CallDepth callDepth) {
//            System.out.println("###pipeline");
//            // Previously we used one unique call depth tracker for all handlers, using
//            // ChannelPipeline.class as a key.
//            // The problem with this approach is that it does not work with netty's
//            // io.netty.channel.ChannelInitializer which provides an `initChannel` that can be used to
//            // `addLast` other handlers. In that case the depth would exceed 0 and handlers added from
//            // initializers would not be considered.
//            // Using the specific handler key instead of the generic ChannelPipeline.class will help us
//            // both to handle such cases and avoid adding our additional handlers in case of internal
//            // calls of `addLast` to other method overloads with a compatible signature.
//            callDepth = CallDepth.forClass(handler.getClass());
//            callDepth.getAndIncrement();
//        }
//
//        @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
//        public static void addHandler(
//                @Advice.This ChannelPipeline pipeline,
//                @Advice.Argument(1) String handlerName,
//                @Advice.Argument(2) ChannelHandler handler,
//                @Advice.Local("otelCallDepth") CallDepth callDepth) {
//            if (callDepth.decrementAndGet() > 0) {
//                return;
//            }
//
//            VirtualField<ChannelHandler, ChannelHandler> instrumentationHandlerField =
//                    VirtualField.find(ChannelHandler.class, ChannelHandler.class);
//
//            // don't add another instrumentation handler if there already is one attached
//            if (instrumentationHandlerField.get(handler) != null) {
//                return;
//            }
//
//            String name = handlerName;
//            if (name == null) {
//                ChannelHandlerContext context = pipeline.context(handler);
//                if (context == null) {
//                    // probably a ChannelInitializer that was used and removed
//                    // see the comment above in @Advice.OnMethodEnter
//                    return;
//                }
//                name = context.name();
//            }
//
//            ChannelHandler ourHandler = null;
//            // Server pipeline handlers
//            if (handler instanceof HttpServerCodec) {
//                ourHandler = new HttpServerTracingHandler();
//            } else if (handler instanceof HttpRequestDecoder) {
//                ourHandler = new HttpServerRequestTracingHandler();
//            } else if (handler instanceof HttpResponseEncoder) {
//                ourHandler = new HttpServerResponseTracingHandler();
//                // Client pipeline handlers
//            } else if (handler instanceof HttpClientCodec) {
//                ourHandler = new HttpClientTracingHandler();
//            } else if (handler instanceof HttpRequestEncoder) {
//                ourHandler = new HttpClientRequestTracingHandler();
//            } else if (handler instanceof HttpResponseDecoder) {
//                ourHandler = new HttpClientResponseTracingHandler();
//                // the SslHandler lives in the netty-handler module, using class name comparison to avoid
//                // adding a dependency
//            } else if (handler.getClass().getName().equals("io.netty.handler.ssl.SslHandler")) {
//                ourHandler = new NettySslInstrumentationHandler(sslInstrumenter(), handler);
//            }
//
//            if (ourHandler != null) {
//                try {
//                    pipeline.addAfter(name, ourHandler.getClass().getName(), ourHandler);
//                    // associate our handle with original handler so they could be removed together
//                    instrumentationHandlerField.set(handler, ourHandler);
//                } catch (IllegalArgumentException e) {
//                    // Prevented adding duplicate handlers.
//                }
//            }
//        }
//    }
//}
